用户多部门切换部门,MySQL根据多个部门id递归获取所有上级(祖级)、获取部门的全路径(全结构名称)

您所在的位置:网站首页 Mysql 递归查询原理 用户多部门切换部门,MySQL根据多个部门id递归获取所有上级(祖级)、获取部门的全路径(全结构名称)

用户多部门切换部门,MySQL根据多个部门id递归获取所有上级(祖级)、获取部门的全路径(全结构名称)

2024-07-16 15:35| 来源: 网络整理| 查看: 265

背景

之前做过的项目,都是一个用户就一个部门的,现在碰到个一个用户在多个部门的需求,而且需要可以切换不同部门查看不同数据。

就比如说一个大公司下面有多个子公司,每个子公司有好多部门、子部门等等,然后有部分用户就和多个子公司/部门都有关联,有些数据就需要根据当前用户选择的部门进行过滤。

实现 1、用户部门关联

首先就是用户和多部门关联,数据库要怎么保存。之前不是单部门的嘛,用户表和部门表关联就是用的一个dept_id字段;现在多部门我也不打算新建表存储关联关系,而是在原有的dept_id字段上,改个数据类型,比如由varchar改为longtext,然后多个部门之间用逗号隔开。

2、切换部门

其次就是切换部门,用户登录的时候查询用户信息,拿到了用户的部门id,根据部门id去递归获取所有上级。

因为我们切换部门的时候每次只能选中一个部门,而且这个部门需要显示全路径(比如:A公司/子公司1/开发部、A公司/子公司1/办公室),所以为了后续好获取全路径,我们需要加一个checked属性,用户的部门id checked值为1,也就是true,其他的上级则为0,也就是false。

递归上级

根据多个部门id,递归所有上级,sql查询如下:

SELECT id,GROUP_CONCAT(DISTINCT `name`) `name`,GROUP_CONCAT(DISTINCT parent_id) parent_id,MAX(checked) checked FROM( WITH RECURSIVE temp_dept(id,name,parent_id,checked) AS ( SELECT id,name,parent_id,1 checked FROM sys_dept WHERE FIND_IN_SET(id,#{deptId}) UNION ALL SELECT d.id,d.name,d.parent_id,0 checked FROM sys_dept d JOIN temp_dept c ON d.id = c.parent_id ) SELECT id,name,parent_id,checked FROM temp_dept ) a GROUP BY id

查询结果用一个实体类接收:

@Data public class DeptVo { private String id; private String parentId; private String name; /** * 部门id全称:1/2/5/13 */ private String fullId; /** * 部门名称全称:A公司/子公司1/开发部/开发一组 */ private String fullName; /** * 是否选中 */ private Boolean checked; public DeptVo() {} public DeptVo(String id, String name, String fullId, String fullName,Boolean checked) { this.id = id; this.name = name; this.fullId = fullId; this.fullName = fullName; this.checked = checked; } } 获取部门的全路径名称

拿到了查询结果,现在我们就可以来处理和获取部门的全路径名称了:

/** * 递归查询,根据当前部门id(可能有多个)获取所有上级 */ public List parentDeptList(String id){ List deptList = baseMapper.parentDeptList(id); List result = new ArrayList(); int count = 0; for (DeptVo node : deptList) { if (node.getChecked()) { generateFullIdAndFullName(deptList, node); // 默认选中第一个部门 if (count == 0) node.setChecked(true); else node.setChecked(false); result.add(node); count++; } } return result; } private static void generateFullIdAndFullName(List nodeList, DeptVo currentNode) { StringBuilder fullIdBuilder = new StringBuilder(); StringBuilder fullNameBuilder = new StringBuilder(); generateFullIdAndFullNameRecursive(nodeList, currentNode, fullIdBuilder, fullNameBuilder); currentNode.setFullId(fullIdBuilder.toString()); currentNode.setFullName(fullNameBuilder.toString()); } private static void generateFullIdAndFullNameRecursive(List nodeList, DeptVo currentNode, StringBuilder fullIdBuilder, StringBuilder fullNameBuilder) { fullIdBuilder.insert(0, currentNode.getId()); fullNameBuilder.insert(0, currentNode.getName()); if (!"ROOT".equals(currentNode.getParentId())) { fullIdBuilder.insert(0, "/"); fullNameBuilder.insert(0, "/"); for (DeptVo node : nodeList) { if (node.getId().equals(currentNode.getParentId())) { generateFullIdAndFullNameRecursive(nodeList, node, fullIdBuilder, fullNameBuilder); break; } } } }

比如说用户A有id为4、5、6、7、8这几个部门,像这种的话就是新增/编辑用户时,用户选择部门时任意一级的部门都可以选,所以就会出现下面图片中的示例:单独选了营销部,但是又选了营销部下面的营销一部。这种情况就不做:切换部门时,查询所在部门及子部门数据。也就是只查询所在部门数据,子部门数据不查询,不然切换部门这个功能的意义不大。

在这里插入图片描述

然后上面的数据的输出结果如下:

在这里插入图片描述

将结果设置到用户信息中

能正确拿到数据后,我们在用户信息中,增加一个字段:

@TableField(exist = false) private List deptArr;

将 parentDeptList 方法返回的数据设置到 deptArr 中,最后将用户信息存储到缓存中。用户登录成功,获取登录用户信息,就可以拿到 deptArr 了,切换时也是在这个列表进行切换选哪一个。

切换部门

切换部门时,前端传选中的部门的id,后端拿到当前登录用户的 deptArr 列表,匹配前端传过来的id,将匹配到的那一条数据的 checked 设置为true,其他的则设置为false:

/** * 切换部门 * @param id 部门id */ @GetMapping(value = "/qhbmsctk") public ResultUtil qhbmsctk(String id,HttpServletRequest request){ // 获取当前登录用户 SysUser loginUser = LoginUtil.getLoginUser(); List deptArr = loginUser.getDeptArr(); List collect = deptArr.stream().peek(obj -> { if (obj.getId().equals(id)) { obj.setChecked(true); // 设置checked为true如果id等于前端传过来的deptId }else { obj.setChecked(false); } }).collect(Collectors.toList()); loginUser.setDeptArr(collect); // 切换成功后,可以重新生成token重新登录(静默登录),也可以将更新后的用户信息重新更新到缓存中 // 看自己实际需求选哪一种方式 return ResultUtil.success(); }

切换成功后,可以重新生成token重新登录(静默登录),也可以将更新后的用户信息重新更新到缓存中,看自己实际需求选哪一种方式。

/** * 获取当前用户选中的部门 */ public DeptVo getCheckedDept(SysUser loginUser){ loginUser = loginUser == null ? LoginUtil.getLoginUser() : loginUser; List list = loginUser.getDeptArr(); DeptVo vo = list.stream().filter(t -> t.getChecked()).findFirst().orElse(null); if (vo == null){ throw new ExceptionVo(-1,"获取当前选中部门失败"); } return vo; } 3、数据过滤

数据过滤有两种过滤方式:1、根据部门id过滤(根据当前用户选中的部门id去过滤数据);2、根据当前用户id过滤(只查询当前用户创建的数据)。

第一种的话,没什么好说的,拿到当前用户选中的部门的id就行。

第二种呢就不能只根据用户id去过滤,因为用户是多部门的,所以需要用 用户id+选中的部门id 去过滤数据。这里的话要过滤数据的表在刚开始设计字段的时候,有两种方式:1、可以设计两个字段:create_id(创建人id)、create_dept(创建人部门id),通过这两个字段去过滤数据;2、也可以只设计一个字段,但是这个字段要包含创建人id和创建人部门id:create_id(由创建人id和创建人部门id组合而成),比如:userid#deptid,两个id之间用某个分隔符隔开,后续哪怕需要单独用的用户id也好、部门id也好,都可以很方便的截取出来。(我后面选择的是第二种方式。)

最后

大家如果有类似的需求要做,可以参考一下,我这里也是记录一下自己的实现方案,方便以后再碰到可以直接把代码拿过来用。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3